package jdir;
import static jdir.util.LogUtil.begin;
import static jdir.util.LogUtil.end;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import jdir.dialog.JDirMainDlg;
import jdir.exceptions.ErrorBuildingJobList;
import jdir.exceptions.NoDirectoryException;
import jdir.exceptions.NotSufficientRightsException;
import jdir.util.Command;
import jdir.util.MessagesUtil;
import jdir.util.PathGen;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This singleton class does all the thinking. It will decide which action will
 * be taken if the user gives commands by clicking and typing in the 
 * {@link jdir.dialog.JDirMainDlg}. And it will start and monitor IO operations.  
 * @author Marco Peroverde
 *
 */
@SuppressWarnings("unused")
public class Control
{
	private static Log				LOG			= LogFactory.getLog(Control.class);
	private static Boolean			DEBUG		= LOG.isDebugEnabled();	
	private static Boolean			TRACE		= LOG.isTraceEnabled();
	private static Control 			instance	= null;
	private static ArrayList<Command>	keyBindings = new ArrayList<Command>();
	
	/**
	 * Only way to get instance of Control class. So it's enforced that we
	 * have only one controling class per JDir Instance.
	 * @return instance of Control
	 * @throws IOException If en error occured during reading of the settings file
	 * @throws FileNotFoundException If the settings file could not be found
	 */
	public static Control getInstance()
	{
		if(instance == null)
		{
			try
			{
				instance = new Control();
			} catch (FileNotFoundException e)
			{
				LOG.error(MessagesUtil.getMessage("error.jdir.control.noSettingsFile") + e.getMessage());
				System.exit(-1);
			} catch (IOException e)
			{
				String str = e.getMessage();
				LOG.error(MessagesUtil.getMessage("error.jdir.control.ioSettingsRead") + e.getMessage());
				System.exit(-1);
			}
		}
		
		return instance;	
	}
	
	private Control() throws FileNotFoundException, IOException
	{
		importKeyBindings();
	}
	
	/**
	 * Every button and every keystroke which triggers an action beyond the panel itself
	 * has a name or a keycode and the corresponding command which is mapped in the keyBindings list 
	 * can beexecuted by calling this method either by providing the keyCode or the name of the button which 
	 * has been pressed. An example for an action which wouldn't be handled through this method would be resorting 
	 * of the filelist. This is an action which doesn't reach beyond the panels. 
	 * @param commandName Unique name of the command triggering button. Will be ignored if keyCode > 0
	 * @param keyCode Unique code for VK events
	 * @param invokerParent Parent of invoker which we can ask who has the inputfocus 
	 */
	public void performCommand(String commandName, int keyCode, JDirMainDlg invokerParent)
	{
		if(DEBUG)
			begin(LOG, commandName, keyCode);
		
		if(keyCode > 0)
		{
			commandName = "";
			for(Command c : keyBindings)
			{
				if(c.getKeyCode() == keyCode )
				{
					commandName = c.getCommandName();
					break;
				}
			}
		}
		
		if(commandName.equals("none"))
		{
			
		}
		else if(commandName.equals("CopyFiles"))
		{
			if(invokerParent.hasLeftSideFocus()) {
				ArrayList<File> selections = (ArrayList) invokerParent.getFileListPanelLeft().getSelectedFiles();
				ArrayList<File> jobList = (ArrayList) generateJobList(selections);
				for(File file : jobList)
					LOG.trace(file.getAbsolutePath());
			}
			
			

		}
		else //default
		{
			if(TRACE)
				LOG.trace(MessagesUtil.getMessage("error.jdir.noCommandFound") + commandName + "KeyCode: " + keyCode);
		}
		
		if(DEBUG)
			end(LOG);
		return;
	}
	
	/**
	 * This method is for getting filelists which can be shown in the @link{FileListPanel}
	 * @param pathObj Path Object which contains a string to the actual directory of interest 
	 * @return List of File objects which represent the given Directory
	 * @throws NoDirectoryException 
	 * @throws NotSufficientRightsException 
	 */
	public File[] generateFileListFor(PathGen pathObj) throws NotSufficientRightsException, NoDirectoryException
	{
		if(DEBUG)
			begin(LOG, pathObj);
			
		if(DEBUG)
			end(LOG, "");
		
		return generateFileListFor(new File(pathObj.getPath()));
	}
	
	/**
	 * This method is for getting filelists which can be shown in the @link{FileListPanel}
	 * @param file File Object for actual directory of interest 
	 * @return List of File objects which represent the given Directory
	 * @throws NoDirectoryException 
	 * @throws NotSufficientRightsException 
	 */
	public File[] generateFileListFor(File file) throws NotSufficientRightsException, NoDirectoryException 
	{
		if(DEBUG)
			begin(LOG, file.getAbsolutePath());
		if(file.isDirectory())
		{
			File fileList[] = file.listFiles();
		
			if(fileList == null) //In this case we hadn't enough rights
			{
				throw new NotSufficientRightsException(file.getAbsolutePath());
			}
			if(DEBUG)
				end(LOG, "");
			return fileList;
		}
		else
		{
			NoDirectoryException e = new NoDirectoryException(file.getAbsolutePath());
			if(TRACE)
				LOG.trace(e.getMessage());
			throw e;
		}
	}
	
	/**
	 * This method reads the keybindings from the settingsfile. It's called only by the Control constructor
	 * @throws IOException If en error occured during reading
	 * @throws FileNotFoundException If the settings file could not be found
	 */
	private void importKeyBindings() throws IOException, FileNotFoundException
	{
		if (DEBUG)
			begin(LOG);
		FileReader fileReader;
		
		fileReader = new FileReader("./settings");		
		BufferedReader bufferedReader = new BufferedReader(fileReader);
		
		String line = "";
		line = bufferedReader.readLine();
		boolean isCommandSectionReached = false;
		while(line != null)
		{
			if(!isCommandSectionReached)
			{
				if(line.equals("<Section Commands>"))
				{
					isCommandSectionReached = true;
				}
			}
			else if(line.length() > 0)
			{
				if(line.equals("</Section Commands>"))
					break;
				try
				{
					keyBindings.add(new Command(Integer.parseInt((line.substring(0, line.indexOf("\t")))),
						line.substring(line.indexOf("\t") + 1, line.length())));
				} catch (Throwable e)
				{
					bufferedReader.close();
					fileReader.close();
					throw new IOException(MessagesUtil.getMessage("error.jdir.control.settingsCorrupt"));
				}
			}
			line = bufferedReader.readLine();
		}
		
		bufferedReader.close();
		fileReader.close();
		
		if (DEBUG)
			end(LOG);
	}
	
	/**
	 * Generates a list of all files which are affected bei the
	 * users selection.
	 * @param selections
	 * @return ArrayList of all files which belong to the job
	 */
	private List<File> generateJobList(List<File> selections) {
		if (DEBUG)
			begin(LOG);
		
		ArrayList<File> jobList = new ArrayList<File>();
		while(selections.size() > 0)
		{
			try {
				addFileToJobList(selections.get(0), jobList);
			} catch (ErrorBuildingJobList e) {
				LOG.error(e.getMessage());
			}
			selections.remove(0);
		}
		
		if (DEBUG)
			end(LOG);
		
		return jobList;
	}
	
	private void addFileToJobList(File file, List<File> jobList) throws ErrorBuildingJobList {
		if (DEBUG)
			begin(LOG, file.getAbsolutePath());
		if(file.isDirectory()) { //If true then look into directory and we are allowed to
			if(file.canRead())
			{
    			File[] content = file.listFiles();
    			for(File contentFile : content) {
    				try {
    					addFileToJobList(contentFile, jobList);
    				} catch (ErrorBuildingJobList e) {
    					LOG.error(e.getMessage());
    				}
    			}
			}
			else
				throw new ErrorBuildingJobList("Not allowed to read file/dir");
		}
		else
			jobList.add(file);
		if (DEBUG)
			end(LOG);
	}
}
